﻿/*
 * Copyright 2014 Francois Karman
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

using Common.Logging;
using System;
using System.Configuration;
using System.Data;
using System.Data.Common;
using System.Linq;

namespace Quartz.WebManager.Logic
{
    /// <summary>
    /// Logs errors and scheduler state into the database.
    /// </summary>
    public class LogSchedulerListener : BaseSchedulerListener, ISchedulerListener
    {
        /// <summary>
        /// The logger associated with the <see cref="LogSchedulerListener"/> class.
        /// </summary>
        private static ILog logger = LogManager.GetLogger<LogSchedulerListener>();

        /// <summary>
        /// The associated scheduler.
        /// </summary>
        private readonly IScheduler scheduler;

        /// <summary>
        /// Prevents an instance of the LogSchedulerListener from being created.
        /// </summary>
        /// <param name="scheduler">The associated scheduler.</param>
        private LogSchedulerListener(IScheduler scheduler)
        {
            this.scheduler = scheduler;
        }

        /// <summary>
        /// Attaches a LogSchedulerListener instance to a specific scheduler.
        /// </summary>
        /// <param name="scheduler">THe associated scheduler.</param>
        public static void Attach(IScheduler scheduler)
        {
            LogSchedulerListener listener = new LogSchedulerListener(scheduler);
            DataAccess.Instance.SchedulerStateInitialize(scheduler.SchedulerInstanceId);
            scheduler.ListenerManager.AddSchedulerListener(listener);
        }

        /// <summary>
        /// Called by the <see cref="IScheduler" /> when a serious error has
        /// occurred within the scheduler - such as repeated failures in the <c>IJobStore</c>,
        /// or the inability to instantiate a <see cref="IJob" /> instance when its
        /// <see cref="ITrigger" /> has fired.
        /// </summary>
        /// <remarks>
        /// Logs the error into the errors table.
        /// </remarks>
        public override void SchedulerError(string msg, SchedulerException cause)
        {
            try
            {
                DataAccess.Instance.SchedulerError(this.scheduler.SchedulerInstanceId, msg, cause);
            }
            catch (Exception exception)
            {
#if DEBUG
                if (System.Diagnostics.Debugger.IsAttached)
                {
                    System.Diagnostics.Debugger.Break();
                }
#endif

                logger.Error("An error occurs and its can't be stored in database", exception);
                logger.ErrorFormat("Details of the initial error: {0}", cause, msg);
            }
        }

        /// <summary>
        /// Called by the <see cref="IScheduler" /> to inform the listener
        /// that it has move to standby mode.
        /// </summary>
        /// <remarks>
        /// Updates the scheduler status table.
        /// </remarks>
        public override void SchedulerInStandbyMode()
        {
            this.SchedulerChangeState("InStandby");
        }

        /// <summary>
        /// Called by the <see cref="IScheduler" /> to inform the listener
        /// that it has started.
        /// </summary>
        /// <remarks>
        /// Updates the scheduler status table.
        /// </remarks>
        public override void SchedulerStarted()
        {
            this.SchedulerChangeState("Started");
        }

        /// <summary>
        /// Called by the <see cref="IScheduler" /> to inform the listener that it is starting.
        /// </summary>
        /// <remarks>
        /// Updates the scheduler status table.
        /// </remarks>
        public override void SchedulerStarting()
        {
            this.SchedulerChangeState("Starting");
        }

        /// <summary> 
        /// Called by the <see cref="IScheduler" /> to inform the listener
        /// that it has Shutdown.
        /// </summary>
        /// <remarks>
        /// Updates the scheduler status table.
        /// </remarks>
        public override void SchedulerShutdown()
        {
            this.SchedulerChangeState("Shutdown");
        }

        /// <summary>
        /// Called by the <see cref="IScheduler" /> to inform the listener
        /// that it has begun the shutdown sequence.
        /// </summary>
        /// <remarks>
        /// Updates the scheduler status table.
        /// </remarks>
        public override void SchedulerShuttingdown()
        {
            this.SchedulerChangeState("ShuttingDown");
        }

        /// <summary>
        /// Saves the change of mode for the current scheduler.
        /// </summary>
        /// <param name="state">The new state of the scheduler.</param>
        private void SchedulerChangeState(string state)
        {
            try
            {
                DataAccess.Instance.SchedulerChangeState(this.scheduler.SchedulerInstanceId, state);
            }
            catch (Exception exception)
            {
#if DEBUG
                if (System.Diagnostics.Debugger.IsAttached)
                {
                    System.Diagnostics.Debugger.Break();
                }
#endif

                string format = "An error occurs when a scheduler change of state ({0}:{1})";
                logger.ErrorFormat(format, exception, this.scheduler.SchedulerInstanceId, state);
            }
        }
    }
}
